home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / mac / DirectX SDK / DXSDK / samples / Multimedia / DirectPlay / SimplePeer / simplepeer.cpp < prev    next >
C/C++ Source or Header  |  2001-10-31  |  29KB  |  718 lines

  1. //----------------------------------------------------------------------------
  2. // File: SimplePeer.cpp
  3. //
  4. // Desc: The main game file for the SimplePeer sample.  It connects 
  5. //       players together with two dialog boxes to prompt users on the 
  6. //       connection settings to join or create a session. After the user 
  7. //       connects to a sesssion, the sample displays a multiplayer stage. 
  8. // 
  9. //       After a new game has started the sample begins a very simplistic 
  10. //       game called "The Greeting Game".  When two or more players are connected
  11. //       to the game, the players have the option of sending a single simple 
  12. //       DirectPlay message to all of the other players. When this message
  13. //       is receieved by the other players, they simply display a dialog box.
  14. //
  15. // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
  16. //-----------------------------------------------------------------------------
  17. #define STRICT
  18. #include <windows.h>
  19. #include <basetsd.h>
  20. #include <dplay8.h>
  21. #include <dplobby8.h>
  22. #include <dxerr8.h>
  23. #include "NetConnect.h"
  24. #include "DXUtil.h"
  25. #include "resource.h"
  26.  
  27.  
  28.  
  29.  
  30. //-----------------------------------------------------------------------------
  31. // Player context locking defines
  32. //-----------------------------------------------------------------------------
  33. CRITICAL_SECTION g_csPlayerContext;
  34. #define PLAYER_LOCK()                   EnterCriticalSection( &g_csPlayerContext ); 
  35. #define PLAYER_ADDREF( pPlayerInfo )    if( pPlayerInfo ) pPlayerInfo->lRefCount++;
  36. #define PLAYER_RELEASE( pPlayerInfo )   if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) SAFE_DELETE( pPlayerInfo ); } pPlayerInfo = NULL;
  37. #define PLAYER_UNLOCK()                 LeaveCriticalSection( &g_csPlayerContext );
  38.  
  39.  
  40. //-----------------------------------------------------------------------------
  41. // Defines, and constants
  42. //-----------------------------------------------------------------------------
  43. #define DPLAY_SAMPLE_KEY        TEXT("Software\\Microsoft\\DirectX DirectPlay Samples")
  44. #define MAX_PLAYER_NAME         14
  45. #define WM_APP_UPDATE_STATS    (WM_APP + 0)
  46. #define WM_APP_DISPLAY_WAVE    (WM_APP + 1)
  47.  
  48. // This GUID allows DirectPlay to find other instances of the same game on
  49. // the network.  So it must be unique for every game, and the same for 
  50. // every instance of that game.  // {02AE835D-9179-485f-8343-901D327CE794}
  51. GUID g_guidApp = { 0x2ae835d, 0x9179, 0x485f, { 0x83, 0x43, 0x90, 0x1d, 0x32, 0x7c, 0xe7, 0x94 } };
  52.  
  53. struct APP_PLAYER_INFO
  54. {
  55.     LONG  lRefCount;                        // Ref count so we can cleanup when all threads 
  56.                                             // are done w/ this object
  57.     DPNID dpnidPlayer;                      // DPNID of player
  58.     TCHAR strPlayerName[MAX_PLAYER_NAME];   // Player name
  59. };
  60.  
  61.  
  62.  
  63.  
  64. //-----------------------------------------------------------------------------
  65. // Global variables
  66. //-----------------------------------------------------------------------------
  67. IDirectPlay8Peer*  g_pDP                         = NULL;    // DirectPlay peer object
  68. CNetConnectWizard* g_pNetConnectWizard           = NULL;    // Connection wizard
  69. IDirectPlay8LobbiedApplication* g_pLobbiedApp    = NULL;    // DirectPlay lobbied app 
  70. BOOL               g_bWasLobbyLaunched           = FALSE;   // TRUE if lobby launched
  71. HINSTANCE          g_hInst                       = NULL;    // HINST of app
  72. HWND               g_hDlg                        = NULL;    // HWND of main dialog
  73. DPNID              g_dpnidLocalPlayer            = 0;       // DPNID of local player
  74. LONG               g_lNumberOfActivePlayers      = 0;       // Number of players currently in game
  75. TCHAR              g_strAppName[256]             = TEXT("SimplePeer");
  76. HRESULT            g_hrDialog;                              // Exit code for app 
  77. TCHAR              g_strLocalPlayerName[MAX_PATH];          // Local player name
  78. TCHAR              g_strSessionName[MAX_PATH];              // Session name
  79. TCHAR              g_strPreferredProvider[MAX_PATH];        // Provider string
  80.  
  81.  
  82.  
  83.  
  84. //-----------------------------------------------------------------------------
  85. // App specific DirectPlay messages and structures 
  86. //-----------------------------------------------------------------------------
  87. #define GAME_MSGID_WAVE        1
  88.  
  89. // Change compiler pack alignment to be BYTE aligned, and pop the current value
  90. #pragma pack( push, 1 )
  91.  
  92. struct GAMEMSG_GENERIC
  93. {
  94.     DWORD dwType;
  95. };
  96.  
  97. // Pop the old pack alignment
  98. #pragma pack( pop )
  99.  
  100.  
  101.  
  102. //-----------------------------------------------------------------------------
  103. // Function-prototypes
  104. //-----------------------------------------------------------------------------
  105. HRESULT WINAPI   DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  106. HRESULT WINAPI   DirectPlayLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  107. INT_PTR CALLBACK GreetingDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  108. HRESULT  InitDirectPlay();
  109. HRESULT  WaveToAllPlayers();
  110. VOID     AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine );
  111.  
  112.  
  113.  
  114.  
  115.  
  116. //-----------------------------------------------------------------------------
  117. // Name: WinMain()
  118. // Desc: Entry point for the application.  Since we use a simple dialog for 
  119. //       user interaction we don't need to pump messages.
  120. //-----------------------------------------------------------------------------
  121. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, 
  122.                       LPSTR pCmdLine, INT nCmdShow )
  123. {
  124.     HRESULT hr;
  125.     HKEY    hDPlaySampleRegKey;
  126.     BOOL    bConnectSuccess = FALSE;
  127.  
  128.     g_hInst = hInst; 
  129.     InitializeCriticalSection( &g_csPlayerContext );
  130.  
  131.     // Read persistent state information from registry
  132.     RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
  133.                     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  134.                     &hDPlaySampleRegKey, NULL );
  135.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), 
  136.                              g_strLocalPlayerName, MAX_PATH, TEXT("TestPlayer") );
  137.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), 
  138.                              g_strSessionName, MAX_PATH, TEXT("TestGame") );
  139.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), 
  140.                              g_strPreferredProvider, MAX_PATH, 
  141.                              TEXT("DirectPlay8 TCP/IP Service Provider") );
  142.  
  143.     // Init COM so we can use CoCreateInstance
  144.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  145.  
  146.     // Create helper class
  147.     g_pNetConnectWizard = new CNetConnectWizard( hInst, NULL, g_strAppName, &g_guidApp );
  148.  
  149.     if( FAILED( hr = InitDirectPlay() ) )
  150.     {
  151.         DXTRACE_ERR( TEXT("InitDirectPlay"), hr );
  152.         MessageBox( NULL, TEXT("Failed initializing IDirectPlay8Peer. ")
  153.                     TEXT("The sample will now quit."),
  154.                     TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  155.         return FALSE;
  156.     }
  157.  
  158.     // If we were launched from a lobby client, then we may have connection settings
  159.     // that we can use either host or join a game.  If not, then we'll need to prompt 
  160.     // the user to detrimine how to connect.
  161.     if( g_bWasLobbyLaunched && g_pNetConnectWizard->HaveConnectionSettingsFromLobby() )
  162.     {
  163.         // If were lobby launched then the DPL_MSGID_CONNECT has already been
  164.         // handled, and since the lobby client also sent us connection settings
  165.         // we can use them to either host or join a DirectPlay session. 
  166.         if( FAILED( hr = g_pNetConnectWizard->ConnectUsingLobbySettings() ) )
  167.         {
  168.             DXTRACE_ERR( TEXT("ConnectUsingLobbySettings"), hr );
  169.             MessageBox( NULL, TEXT("Failed to connect using lobby settings. ")
  170.                         TEXT("The sample will now quit."),
  171.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  172.  
  173.             bConnectSuccess = FALSE;
  174.         }
  175.         else
  176.         {
  177.             // Read information from g_pNetConnectWizard
  178.             _tcscpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName() );
  179.  
  180.             bConnectSuccess = TRUE; 
  181.         }
  182.     }
  183.     else
  184.     {
  185.         // If not lobby launched, prompt the user about the network 
  186.         // connection and which session they would like to join or 
  187.         // if they want to create a new one.
  188.  
  189.         // Setup connection wizard
  190.         g_pNetConnectWizard->SetPlayerName( g_strLocalPlayerName );
  191.         g_pNetConnectWizard->SetSessionName( g_strSessionName );
  192.         g_pNetConnectWizard->SetPreferredProvider( g_strPreferredProvider );
  193.  
  194.         // Start a connection wizard.  The wizard uses GDI dialog boxes.
  195.         // More complex games can use this as a starting point and add a 
  196.         // fancier graphics layer such as Direct3D.
  197.         hr = g_pNetConnectWizard->DoConnectWizard( FALSE );        
  198.         if( FAILED( hr ) ) 
  199.         {
  200.             DXTRACE_ERR( TEXT("DoConnectWizard"), hr );
  201.             MessageBox( NULL, TEXT("Multiplayer connect failed. ")
  202.                         TEXT("The sample will now quit."),
  203.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  204.             bConnectSuccess = FALSE;
  205.         } 
  206.         else if( hr == NCW_S_QUIT ) 
  207.         {
  208.             // The user canceled the Multiplayer connect, so quit 
  209.             bConnectSuccess = FALSE;
  210.         }
  211.         else
  212.         {
  213.             bConnectSuccess = TRUE; 
  214.  
  215.             // Read information from g_pNetConnectWizard
  216.             _tcscpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName() );
  217.             _tcscpy( g_strSessionName, g_pNetConnectWizard->GetSessionName() );
  218.             _tcscpy( g_strPreferredProvider, g_pNetConnectWizard->GetPreferredProvider() );
  219.  
  220.             // Write information to the registry
  221.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), g_strLocalPlayerName );
  222.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), g_strSessionName );
  223.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), g_strPreferredProvider );
  224.         }
  225.     }
  226.  
  227.     if( bConnectSuccess )
  228.     {
  229.         // App is now connected via DirectPlay, so start the game.  
  230.  
  231.         // For this sample, we just start a simple dialog box game.
  232.         g_hrDialog = S_OK;
  233.         DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN_GAME), NULL, 
  234.                    (DLGPROC) GreetingDlgProc );
  235.  
  236.         if( FAILED( g_hrDialog ) )
  237.         {
  238.             if( g_hrDialog == DPNERR_CONNECTIONLOST )
  239.             {
  240.                 MessageBox( NULL, TEXT("The DirectPlay session was lost. ")
  241.                             TEXT("The sample will now quit."),
  242.                             TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  243.             }
  244.             else
  245.             {
  246.                 DXTRACE_ERR( TEXT("DialogBox"), g_hrDialog );
  247.                 MessageBox( NULL, TEXT("An error occured during the game. ")
  248.                             TEXT("The sample will now quit."),
  249.                             TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  250.             }
  251.         }
  252.     }
  253.  
  254.     // Cleanup DirectPlay and helper classes
  255.     g_pNetConnectWizard->Shutdown();
  256.  
  257.     if( g_pDP )
  258.     {
  259.         g_pDP->Close(0);
  260.         SAFE_RELEASE( g_pDP );
  261.     }
  262.  
  263.     if( g_pLobbiedApp )
  264.     {
  265.         g_pLobbiedApp->Close( 0 );
  266.         SAFE_RELEASE( g_pLobbiedApp );
  267.     }    
  268.  
  269.     // Don't delete the wizard until we know that 
  270.     // DirectPlay is out of its message handlers.
  271.     // This will be true after Close() has been called. 
  272.     SAFE_DELETE( g_pNetConnectWizard );
  273.  
  274.     RegCloseKey( hDPlaySampleRegKey );
  275.     DeleteCriticalSection( &g_csPlayerContext );
  276.     CoUninitialize();
  277.  
  278.     return TRUE;
  279. }
  280.  
  281.  
  282.  
  283.  
  284. //-----------------------------------------------------------------------------
  285. // Name: InitDirectPlay()
  286. // Desc: 
  287. //-----------------------------------------------------------------------------
  288. HRESULT InitDirectPlay()
  289. {
  290.     DPNHANDLE hLobbyLaunchedConnection = NULL;
  291.     HRESULT hr;
  292.  
  293.     // Create IDirectPlay8Peer
  294.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL, 
  295.                                        CLSCTX_INPROC_SERVER,
  296.                                        IID_IDirectPlay8Peer, 
  297.                                        (LPVOID*) &g_pDP ) ) )
  298.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  299.  
  300.     // Create IDirectPlay8LobbiedApplication
  301.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8LobbiedApplication, NULL, 
  302.                                        CLSCTX_INPROC_SERVER,
  303.                                        IID_IDirectPlay8LobbiedApplication, 
  304.                                        (LPVOID*) &g_pLobbiedApp ) ) )
  305.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  306.  
  307.     // Init the helper class, now that g_pDP and g_pLobbiedApp are valid
  308.     g_pNetConnectWizard->Init( g_pDP, g_pLobbiedApp );
  309.  
  310.     // Init IDirectPlay8Peer
  311.     if( FAILED( hr = g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
  312.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  313.  
  314.     // Init IDirectPlay8LobbiedApplication.  Before this Initialize() returns 
  315.     // a DPL_MSGID_CONNECT msg may come in to the DirectPlayLobbyMessageHandler 
  316.     // so be prepared ahead of time.
  317.     if( FAILED( hr = g_pLobbiedApp->Initialize( NULL, DirectPlayLobbyMessageHandler, 
  318.                                                 &hLobbyLaunchedConnection, 0 ) ) )
  319.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  320.  
  321.     // IDirectPlay8LobbiedApplication::Initialize returns a handle to a connnection
  322.     // if we have been lobby launced.  Initialize is guanteeded to return after 
  323.     // the DPL_MSGID_CONNECT msg has been processed.  So unless a we are expected 
  324.     // multiple lobby connections, we do not need to remember the lobby connection
  325.     // handle since it will be recorded upon the DPL_MSGID_CONNECT msg.
  326.     g_bWasLobbyLaunched = ( hLobbyLaunchedConnection != NULL );
  327.  
  328.     return S_OK;
  329. }
  330.  
  331.  
  332.  
  333.  
  334. //-----------------------------------------------------------------------------
  335. // Name: GreetingDlgProc()
  336. // Desc: Handles dialog messages
  337. //-----------------------------------------------------------------------------
  338. INT_PTR CALLBACK GreetingDlgProc( HWND hDlg, UINT msg, 
  339.                                   WPARAM wParam, LPARAM lParam )
  340. {
  341.     switch( msg ) 
  342.     {
  343.         case WM_INITDIALOG:
  344.         {
  345.             g_hDlg = hDlg;
  346.  
  347.             // Load and set the icon
  348.             HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  349.             SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  350.             SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  351.  
  352.             if( g_pNetConnectWizard->IsHostPlayer() )
  353.                 SetWindowText( hDlg, TEXT("SimplePeer (Host)") );
  354.             else
  355.                 SetWindowText( hDlg, TEXT("SimplePeer") );
  356.  
  357.             // Display local player's name
  358.             SetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strLocalPlayerName );
  359.  
  360.             PostMessage( hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  361.             break;
  362.         }
  363.  
  364.         case WM_APP_UPDATE_STATS:
  365.         {
  366.             // Update the number of players in the game
  367.             TCHAR strNumberPlayers[32];
  368.  
  369.             wsprintf( strNumberPlayers, TEXT("%d"), g_lNumberOfActivePlayers );
  370.             SetDlgItemText( hDlg, IDC_NUM_PLAYERS, strNumberPlayers );
  371.             break;
  372.         }
  373.  
  374.         case WM_APP_DISPLAY_WAVE:
  375.         {
  376.             HRESULT          hr;
  377.             DPNID            dpnidPlayer = (DWORD)wParam;
  378.             APP_PLAYER_INFO* pPlayerInfo = NULL;
  379.             
  380.             PLAYER_LOCK(); // enter player context CS
  381.  
  382.             // Get the player context accosicated with this DPNID
  383.             hr = g_pDP->GetPlayerContext( dpnidPlayer, 
  384.                                           (LPVOID* const) &pPlayerInfo,
  385.                                           0);
  386.  
  387.             PLAYER_ADDREF( pPlayerInfo ); // addref player, since we are using it now
  388.             PLAYER_UNLOCK(); // leave player context CS
  389.  
  390.             if( FAILED(hr) || pPlayerInfo == NULL )
  391.             {
  392.                 // The player who sent this may have gone away before this 
  393.                 // message was handled, so just ignore it
  394.                 break;
  395.             }
  396.             
  397.             // Make wave message and display it.
  398.             TCHAR szWaveMessage[MAX_PATH];
  399.             wsprintf( szWaveMessage, TEXT("%s just waved at you, %s!\r\n"), 
  400.                       pPlayerInfo->strPlayerName, g_strLocalPlayerName );
  401.  
  402.             PLAYER_LOCK();
  403.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  404.             PLAYER_UNLOCK();
  405.  
  406.             AppendTextToEditControl( hDlg, szWaveMessage );
  407.             break;
  408.         }
  409.  
  410.         case WM_COMMAND:
  411.         {
  412.             switch( LOWORD(wParam) )
  413.             {
  414.                 case IDC_WAVE:
  415.                     if( FAILED( g_hrDialog = WaveToAllPlayers() ) )
  416.                     {
  417.                         DXTRACE_ERR( TEXT("WaveToAllPlayers"), g_hrDialog );
  418.                         EndDialog( hDlg, 0 );
  419.                     }
  420.  
  421.                     return TRUE;
  422.  
  423.                 case IDCANCEL:
  424.                     g_hrDialog = S_OK;
  425.                     EndDialog( hDlg, 0 );
  426.                     return TRUE;
  427.             }
  428.             break;
  429.         }
  430.     }
  431.  
  432.     return FALSE; // Didn't handle message
  433. }
  434.  
  435.  
  436.  
  437.  
  438. //-----------------------------------------------------------------------------
  439. // Name: DirectPlayMessageHandler
  440. // Desc: Handler for DirectPlay messages.  This function is called by
  441. //       the DirectPlay message handler pool of threads, so be careful of thread
  442. //       synchronization problems with shared memory
  443. //-----------------------------------------------------------------------------
  444. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, 
  445.                                          DWORD dwMessageId, 
  446.                                          PVOID pMsgBuffer )
  447. {
  448.     // Try not to stay in this message handler for too long, otherwise
  449.     // there will be a backlog of data.  The best solution is to 
  450.     // queue data as it comes in, and then handle it on other threads.
  451.     
  452.     // This function is called by the DirectPlay message handler pool of 
  453.     // threads, so be careful of thread synchronization problems with shared memory
  454.  
  455.     switch( dwMessageId )
  456.     {
  457.         case DPN_MSGID_CREATE_PLAYER:
  458.         {
  459.             HRESULT hr;
  460.             PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
  461.             pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER)pMsgBuffer;
  462.  
  463.             // Create a new and fill in a APP_PLAYER_INFO
  464.             APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
  465.             ZeroMemory( pPlayerInfo, sizeof(APP_PLAYER_INFO) );
  466.             pPlayerInfo->lRefCount   = 1;
  467.             pPlayerInfo->dpnidPlayer = pCreatePlayerMsg->dpnidPlayer;
  468.  
  469.             // Get the peer info and extract its name
  470.             DWORD dwSize = 0;
  471.             DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
  472.             hr = DPNERR_CONNECTING;
  473.             
  474.             // GetPeerInfo might return DPNERR_CONNECTING when connecting, 
  475.             // so just keep calling it if it does
  476.             while( hr == DPNERR_CONNECTING ) 
  477.                 hr = g_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );                                
  478.                 
  479.             if( hr == DPNERR_BUFFERTOOSMALL )
  480.             {
  481.                 pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
  482.                 ZeroMemory( pdpPlayerInfo, dwSize );
  483.                 pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
  484.                 
  485.                 hr = g_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
  486.                 if( SUCCEEDED(hr) )
  487.                 {
  488.                     // This stores a extra TCHAR copy of the player name for 
  489.                     // easier access.  This will be redundent copy since DPlay 
  490.                     // also keeps a copy of the player name in GetPeerInfo()
  491.                     DXUtil_ConvertWideStringToGeneric( pPlayerInfo->strPlayerName, 
  492.                                                        pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );    
  493.                                                        
  494.                     if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL )
  495.                         g_dpnidLocalPlayer = pCreatePlayerMsg->dpnidPlayer;       
  496.                 }
  497.                 
  498.                 SAFE_DELETE_ARRAY( pdpPlayerInfo );
  499.             }
  500.  
  501.             // Tell DirectPlay to store this pPlayerInfo 
  502.             // pointer in the pvPlayerContext.
  503.             pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;
  504.  
  505.             // Update the number of active players, and 
  506.             // post a message to the dialog thread to update the 
  507.             // UI.  This keeps the DirectPlay message handler 
  508.             // from blocking
  509.             InterlockedIncrement( &g_lNumberOfActivePlayers );
  510.             if( g_hDlg != NULL )
  511.                 PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  512.  
  513.             break;
  514.         }
  515.  
  516.         case DPN_MSGID_DESTROY_PLAYER:
  517.         {
  518.             PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
  519.             pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
  520.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;
  521.  
  522.             PLAYER_LOCK();                  // enter player context CS
  523.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  524.             PLAYER_UNLOCK();                // leave player context CS
  525.  
  526.             // Update the number of active players, and 
  527.             // post a message to the dialog thread to update the 
  528.             // UI.  This keeps the DirectPlay message handler 
  529.             // from blocking
  530.             InterlockedDecrement( &g_lNumberOfActivePlayers );
  531.             if( g_hDlg != NULL )
  532.                 PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  533.  
  534.             break;
  535.         }
  536.  
  537.         case DPN_MSGID_HOST_MIGRATE:
  538.         {
  539.             PDPNMSG_HOST_MIGRATE pHostMigrateMsg;
  540.             pHostMigrateMsg = (PDPNMSG_HOST_MIGRATE)pMsgBuffer;
  541.  
  542.             // Check to see if we are the new host
  543.             if( pHostMigrateMsg->dpnidNewHost == g_dpnidLocalPlayer )
  544.                 SetWindowText( g_hDlg, TEXT("SimplePeer (Host)") );
  545.             break;
  546.         }
  547.  
  548.         case DPN_MSGID_TERMINATE_SESSION:
  549.         {
  550.             PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
  551.             pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
  552.  
  553.             g_hrDialog = DPNERR_CONNECTIONLOST;
  554.             EndDialog( g_hDlg, 0 );
  555.             break;
  556.         }
  557.  
  558.         case DPN_MSGID_RECEIVE:
  559.         {
  560.             PDPNMSG_RECEIVE pReceiveMsg;
  561.             pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
  562.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;
  563.             if( NULL == pPlayerInfo )
  564.                 break;
  565.  
  566.             GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
  567.             if( pMsg->dwType == GAME_MSGID_WAVE )
  568.             {
  569.                 // This message is sent when a player has waved to us, so 
  570.                 // post a message to the dialog thread to update the UI.  
  571.                 // This keeps the DirectPlay threads from blocking, and also
  572.                 // serializes the recieves since DirectPlayMessageHandler can
  573.                 // be called simultaneously from a pool of DirectPlay threads.
  574.                 PostMessage( g_hDlg, WM_APP_DISPLAY_WAVE, pPlayerInfo->dpnidPlayer, 0 );
  575.             }
  576.             break;
  577.         }
  578.     }
  579.  
  580.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  581.     // so it can be informed of messages such as DPN_MSGID_ENUM_HOSTS_RESPONSE.
  582.     if( g_pNetConnectWizard )
  583.         return g_pNetConnectWizard->MessageHandler( pvUserContext, dwMessageId, 
  584.                                                     pMsgBuffer );
  585.     
  586.     return S_OK;
  587. }
  588.  
  589.  
  590.  
  591.  
  592. //-----------------------------------------------------------------------------
  593. // Name: DirectPlayLobbyMessageHandler
  594. // Desc: Handler for DirectPlay lobby messages.  This function is called by
  595. //       the DirectPlay lobby message handler pool of threads, so be careful of 
  596. //       thread synchronization problems with shared memory
  597. //-----------------------------------------------------------------------------
  598. HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, 
  599.                                               DWORD dwMessageId, 
  600.                                               PVOID pMsgBuffer )
  601. {
  602.     switch( dwMessageId )
  603.     {
  604.         case DPL_MSGID_CONNECT:
  605.         {
  606.             PDPL_MESSAGE_CONNECT pConnectMsg;
  607.             pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
  608.  
  609.             // The CNetConnectWizard will handle this message for us,
  610.             // so there is nothing we need to do here for this simple
  611.             // sample.
  612.             break;
  613.         }
  614.  
  615.         case DPL_MSGID_DISCONNECT:
  616.         {
  617.             PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
  618.             pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)pMsgBuffer;
  619.  
  620.             // We should free any data associated with the lobby 
  621.             // client here, but there is none.
  622.             break;
  623.         }
  624.  
  625.         case DPL_MSGID_RECEIVE:
  626.         {
  627.             PDPL_MESSAGE_RECEIVE pReceiveMsg;
  628.             pReceiveMsg = (PDPL_MESSAGE_RECEIVE)pMsgBuffer;
  629.  
  630.             // The lobby client sent us data.  This sample doesn't
  631.             // expected data from the client, but it is useful 
  632.             // for more complex apps.
  633.             break;
  634.         }
  635.  
  636.         case DPL_MSGID_CONNECTION_SETTINGS:
  637.         {
  638.             PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
  639.             pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)pMsgBuffer;
  640.  
  641.             // The lobby client has changed the connection settings.  
  642.             // This simple sample doesn't handle this, but more complex apps may
  643.             // want to.
  644.             break;
  645.         }
  646.     }
  647.  
  648.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  649.     // so the wizard can be informed of lobby messages such as DPL_MSGID_CONNECT
  650.     if( g_pNetConnectWizard )
  651.         return g_pNetConnectWizard->LobbyMessageHandler( pvUserContext, dwMessageId, 
  652.                                                          pMsgBuffer );
  653.     
  654.     return S_OK;
  655. }
  656.  
  657.  
  658.  
  659.  
  660. //-----------------------------------------------------------------------------
  661. // Name: WaveToAllPlayers()
  662. // Desc: Send a app-defined "wave" DirectPlay message to all connected players
  663. //-----------------------------------------------------------------------------
  664. HRESULT WaveToAllPlayers()
  665. {
  666.     // This is called by the dialog UI thread.  This will send a message to all
  667.     // the players or inform the player that there is no one to wave at.
  668.     if( g_lNumberOfActivePlayers == 1 )
  669.     {
  670.         MessageBox( NULL, TEXT("No one is around to wave at! :("), 
  671.                     TEXT("SimplePeer"), MB_OK );
  672.     }
  673.     else
  674.     {
  675.         // Send a message to all of the players
  676.         GAMEMSG_GENERIC msgWave;
  677.         msgWave.dwType = GAME_MSGID_WAVE;
  678.  
  679.         DPN_BUFFER_DESC bufferDesc;
  680.         bufferDesc.dwBufferSize = sizeof(GAMEMSG_GENERIC);
  681.         bufferDesc.pBufferData  = (BYTE*) &msgWave;
  682.  
  683.         DPNHANDLE hAsync;
  684.         g_pDP->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
  685.                        0, NULL, &hAsync, DPNSEND_NOLOOPBACK | DPNSEND_GUARANTEED );
  686.     }
  687.  
  688.     return S_OK;
  689. }
  690.  
  691.  
  692.  
  693.  
  694. //-----------------------------------------------------------------------------
  695. // Name: AppendTextToEditControl()
  696. // Desc: Appends a string of text to the edit control
  697. //-----------------------------------------------------------------------------
  698. VOID AppendTextToEditControl( HWND hDlg, TCHAR* strNewLogLine )
  699. {
  700.     static TCHAR strText[1024*10];
  701.  
  702.     HWND hEdit = GetDlgItem( hDlg, IDC_LOG_EDIT );
  703.     SendMessage( hEdit, WM_SETREDRAW, FALSE, 0 );
  704.     GetWindowText( hEdit, strText, 1024*9 );
  705.  
  706.     _tcscat( strText, strNewLogLine );
  707.  
  708.     int nSecondLine = 0;
  709.     if( SendMessage( hEdit, EM_GETLINECOUNT, 0, 0 ) > 9 )
  710.         nSecondLine = (int)SendMessage( hEdit, EM_LINEINDEX, 1, 0 );
  711.  
  712.     SetWindowText( hEdit, &strText[nSecondLine] );
  713.  
  714.     SendMessage( hEdit, WM_SETREDRAW, TRUE, 0 );
  715.     InvalidateRect( hEdit, NULL, TRUE );
  716.     UpdateWindow( hEdit );
  717. }
  718.